package com.jgp.common.utils;

import org.apache.commons.lang3.reflect.FieldUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.persistence.Column;
import javax.persistence.Transient;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 项目   jgp
 * 作者   loufei
 * 时间   2017/7/13
 */
public class ReflectUtil {
    private final static Logger logger = LoggerFactory.getLogger(ReflectUtil.class);
    
    /**
     * 根据属性名获取属性值
     *
     * @param o         被操作对象
     * @param fieldName 属性名
     * @return 值
     */
    public static Object getFieldValueByName(Object o, String fieldName) {
        try {
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            String getter = "get" + firstLetter + fieldName.substring(1);
            Method method = o.getClass().getMethod(getter);
            Object value = method.invoke(o);
            return value;
        } catch (Exception e) {
            return null;
        }
    }
    
    public static Class getDeclaredFieldType(Class clazz, String fieldName) {
        Field field = null;
        
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
            try {
                field = clazz.getDeclaredField(fieldName);
                return field.getType();
            } catch (Exception e) {
                //这里甚么都不能抛出去。
                //如果这里的异常打印或者往外抛，则就不会进入
            }
        }
        return null;
    }
    
    /**
     * 获取属性名数组
     *
     * @param o 被获取对象
     * @return 属性名数组
     */
    public static String[] getFiledName(Object o) {
        Field[] fields = o.getClass().getDeclaredFields();
        String[] fieldNames = new String[fields.length];
        for (int i = 0; i < fields.length; i++) {
            System.out.println(fields[i].getType());
            fieldNames[i] = fields[i].getName();
        }
        return fieldNames;
    }
    
    /**
     * 获取Class 或 对象的所有属性
     *
     * @param o Class或对象
     * @return 属性数组
     */
    public static Field[] getFields(Object o) {
        Field[] fields = null;
        if (o instanceof Class) {
            fields = ((Class) o).getDeclaredFields();
        } else {
            fields = o.getClass().getDeclaredFields();
        }
        return fields;
    }
    
    public static Map<String, Field> getFieldMap(Class<?> clazz) {
        if (null == clazz) {
            return null;
        }
        Map<String, Field> fieldList = new LinkedHashMap<>();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            /** 过滤静态属性**/
            if (Modifier.isStatic(field.getModifiers())) {
                continue;
            }
            /** 过滤transient 关键字修饰的属性**/
            if (Modifier.isTransient(field.getModifiers())) {
                continue;
            }
            fieldList.put(field.getName(), field);
        }
        /** 处理父类字段**/
        Class<?> superClass = clazz.getSuperclass();
        if (superClass.equals(Object.class)) {
            return fieldList;
        }
        fieldList.putAll(getFieldMap(superClass));
        return fieldList;
    }
    
    /**
     * 获取对象的属性 或 属性和值
     *
     * @param o         被获取对象
     * @param needValue 是否需要属性值
     * @return List&lt;Map&gt; Map =&gt; type,name
     */
    public static List getFieldsInfo(Object o, boolean needValue) {
        Field[] fields = getFields(o);
        List list = new ArrayList();
        for (int i = 0; i < fields.length; i++) {
            Map infoMap = new HashMap();
            infoMap.put("type", fields[i].getType());
            infoMap.put("name", fields[i].getName());
            if (needValue) {
                infoMap.put("value", getFieldValueByName(o, fields[i].getName()));
            }
            list.add(infoMap);
        }
        return list;
    }
    
    
    /**
     * 获取对象属性名和属性类型
     *
     * @param o 被操作对象
     * @return key为属性名 value 为属性类型
     */
    public static Map<String, Class> getFieldsType(Object o) {
        Field[] fields = getFields(o);
        Map<String, Class> map = new HashMap<>();
        for (int i = 0; i < fields.length; i++) {
            map.put(fields[i].getName(), fields[i].getType());
        }
        return map;
    }
    
    /**
     * 获取对象的所有属性值，返回一个数组
     *
     * @param o 被操作对象
     * @return 属性值数组
     */
    public static Object[] getFiledValues(Object o) {
        String[] fieldNames = getFiledName(o);
        Object[] value = new Object[fieldNames.length];
        for (int i = 0; i < fieldNames.length; i++) {
            value[i] = getFieldValueByName(o, fieldNames[i]);
        }
        return value;
    }
    
    public static <A> A getFieldAnnotation(Class annotationClass, String fieldName, Object o) {
        Field field;
        Class clazz = null;
        try {
            if (o instanceof Class) {
                clazz = (Class) o;
            } else {
                clazz = o.getClass();
            }
            field = clazz.getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            Class clz = clazz.getSuperclass();
            try {
                field = clz.getDeclaredField(fieldName);
            } catch (NoSuchFieldException e1) {
                return null;
            }
        }
        field.setAccessible(true);
        return (A) field.getAnnotation(annotationClass);
    }
    
    public static Object cast(Object value, Class clazz) throws RuntimeException {
        if (null == value) return null;
        String val = value.toString();
        try {
            if (Long.class.isAssignableFrom(clazz) || long.class.isAssignableFrom(clazz)) {
                return Long.parseLong(val);
            } else if (Integer.class.isAssignableFrom(clazz) || int.class.isAssignableFrom(clazz)) {
                return Integer.parseInt(val);
            } else if (Float.class.isAssignableFrom(clazz) || float.class.isAssignableFrom(clazz)) {
                return Float.parseFloat(val);
            } else if (Double.class.isAssignableFrom(clazz) || double.class.isAssignableFrom(clazz)) {
                return Double.parseDouble(val);
            } else if (BigDecimal.class.isAssignableFrom(clazz)) {
                return new BigDecimal(val);
            } else if (Boolean.class.isAssignableFrom(clazz) || boolean.class.isAssignableFrom(clazz)) {
                return Boolean.parseBoolean(val);
            } else if (Date.class.isAssignableFrom(clazz)) {
                return JGPUtil.S_DATE_TIME_FORMATTER.parse(val);
            } else if (Timestamp.class.isAssignableFrom(clazz)) {
                Date date = JGPUtil.S_DATE_TIME_FORMATTER.parse(val);
                return Timestamp.from(date.toInstant());
            } else if (LocalDate.class.isAssignableFrom(clazz)) {
                return LocalDate.parse(val, JGPUtil.DATE_FORMATTER);
            } else if (LocalDateTime.class.isAssignableFrom(clazz)) {
                return LocalDateTime.parse(val.length() > 19 ? val.substring(0, 19) : val, JGPUtil.DATE_TIME_FORMATTER);
            } else if (LocalTime.class.isAssignableFrom(clazz)) {
                return LocalTime.parse(val, JGPUtil.TIME_FORMATTER);
            } else if (Enum.class.isAssignableFrom(clazz)) {
                return Enum.valueOf((Class<Enum>) clazz, val);
            } else if (List.class.isAssignableFrom(clazz)) {
                return Arrays.asList(val.split(","));
            } else if (Set.class.isAssignableFrom(clazz)) {
                return new HashSet(Arrays.asList(val.split(",")));
            } else {
                return val;
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("类型转换失败！", e);
            throw new RuntimeException(e);
        }
    }
    
    /**
     * 获取持久化类中所有被@Column，@Transient注解修饰的属性名称和类型
     * 包括父类中的属性
     *
     * @param target 持久化类 即 数据库模型
     * @return 返回List&lt;String[]&gt; String[] =&gt; [属性名,类型名]
     */
    public static List<Object[]> getPersistFieldNames(Class<?> target) {
        List<Field> fields = FieldUtils.getAllFieldsList(target);
        return fields.stream().filter(f -> {
            Annotation column = f.getAnnotation(Column.class);
            Annotation trans = f.getAnnotation(Transient.class);
            return Objects.nonNull(column) || Objects.nonNull(trans);
        }).map(f -> new Object[]{f.getName(), f.getType(), f.getDeclaredAnnotation(Column.class), f.getAnnotations()}).collect(Collectors.toList());
    }
    
    public static List<Field> getPersistFields(Class<?> target) {
        List<Field> fields = FieldUtils.getAllFieldsList(target);
        return fields.stream().filter(f -> {
            Annotation column = f.getAnnotation(Column.class);
            return Objects.nonNull(column);
        }).collect(Collectors.toList());
    }
    
    /*private static List<Object[]> getPersistFieldNames(Field[] fields) {
        return Arrays.asList(fields).stream().filter((field) -> {
            Annotation column = field.getAnnotation(Column.class);
            Annotation trans = field.getAnnotation(Transient.class);
            return Objects.nonNull(column) && Objects.isNull(trans);
        }).map((field) -> new Object[]{field.getName(), field.getType(), field.getDeclaredAnnotation(
                Column.class), field.getAnnotations()}).collect(Collectors.toList());
    }*/
    
    /**
     * 运行时判断泛型类型代表的是不是目标类型
     *
     * @param generic 泛型  比如 Service&lt;T&gt; T则是泛型类型
     * @param target  目标类型
     * @return true/false
     */
    public static boolean checkGeneric(ParameterizedType generic, Class target) {
        return target.equals(generic.getRawType());
    }
    
    /**
     * 运行时返回目标类型的泛型类型
     *
     * @param target 泛型  比如 Service&lt;T&gt; T则是泛型类型
     * @param index  第几个
     * @return 泛型类型
     */
    public static Class<Object> getGenericSuperclassType(final Class target, final int index) {
        
        //返回表示此 Class 所表示的实体（类、接口、基本类型或 void）的直接超类的 Type。
        Type genType = target.getGenericSuperclass();
        
        if (!(genType instanceof ParameterizedType)) {
            return Object.class;
        }
        //返回表示此类型实际类型参数的 Type 对象的数组。
        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
        
        if (index >= params.length || index < 0) {
            return Object.class;
        }
        if (!(params[index] instanceof Class)) {
            return Object.class;
        }
        
        return (Class) params[index];
    }
    
    
    /**
     * Class中的属性是否被指定注解修饰
     *
     * @param target    对象或class
     * @param fieldName 属性名
     * @param an        注解类型
     * @return true/false
     */
    public static boolean checkFieldAnnotation(final Class target, final String fieldName, Class an) {
        List<Field> fields = FieldUtils.getAllFieldsList(target);
        long count = fields.stream().filter(f -> fieldName.equals(f.getName()) && Objects.nonNull(f.getAnnotation(an))).count();
        
        return count > 0;
    }
    
    /**
     * 循环向上转型, 获取对象的 DeclaredField
     *
     * @param object    : 子类对象
     * @param fieldName : 父类中的属性名
     * @return 父类中的属性对象
     */
    
    public static Field getDeclaredField(Object object, String fieldName) {
        Field field = null;
        
        Class<?> clazz = object.getClass();
        
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
            try {
                field = clazz.getDeclaredField(fieldName);
                return field;
            } catch (Exception e) {
                //logger.error("获取属性失败{" + fieldName + "}");
            }
        }
        
        return null;
    }
    
    /**
     * 直接设置对象属性值, 忽略 private/protected 修饰符, 也不经过 setter
     *
     * @param object    : 子类对象
     * @param fieldName : 父类中的属性名
     * @param value     : 将要设置的值
     */
    public static void setFieldValue(Object object, String fieldName, Object value) {
        
        // 根据 对象和属性名通过反射 调用上面的方法获取 Field对象
        Field field = getDeclaredField(object, fieldName);
        
        // 抑制Java对其的检查
        field.setAccessible(true);
        try {
            // 将 object 中 field 所代表的值 设置为 value
            field.set(object, value);
        } catch (IllegalArgumentException | IllegalAccessException e) {
            logger.error("动态设置{" + fieldName + "}对象属性失败", e);
        }
    }
    
  /*  public static <M extends Model> M newInstance(Class<M> clazz, Map<String, Object> fields) throws
            IllegalAccessException, InstantiationException {
        M m = clazz.newInstance();
        fields.forEach((fieldName, value) -> {
            setFieldValue(m, fieldName, value);
        });
        return m;
    }*/
    
    public static Type getFieldType(Class<?> clazz, String fieldName) throws NoSuchFieldException {
        return clazz.getDeclaredField(fieldName).getType();
    }
    
    public static Object invoke(String clazzName, String methodName, Class[] paramsClazz, Object... params) throws ClassNotFoundException,
            IllegalAccessException, InstantiationException, InvocationTargetException {
        Method method = getMethod(Class.forName(clazzName), methodName, paramsClazz, params);
        Class<?> clazz = Class.forName(clazzName);
        if (Objects.nonNull(method)) {
            return method.invoke(clazz.newInstance(), params);
        } else {
            return null;
        }
    }
    
    public static Method getMethod(Class clazz, String methodName, Class[] paramsClazz, Object... params) {
        Method method;
        try {
            method = clazz.getDeclaredMethod(methodName, paramsClazz);
        } catch (NoSuchMethodException e) {
            if (clazz != Object.class) {
                method = getMethod(clazz.getSuperclass(), methodName, paramsClazz, params);
            } else {
                method = null;
            }
        }
        return method;
    }
    
    /**
     * 判断是否为JAVA基本类型
     *
     * @param clz
     * @return
     */
    public static boolean isBaseClass(Class clz) {
        try {
            return ((Class) clz.getField("TYPE").get(null)).isPrimitive();
        } catch (Exception e) {
            return false;
        }
    }
    
}
